﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace WindowsFormsApplication1
{
    public class ServerListener
    {
        private MSLogger _logger;
        private TcpListener _socketListener;
        private Thread _thread;
        private ManualResetEvent _event;
        private object _synch;
        private ArrayList _sessions;
        private int _maxSocketSessions = 20;
        private int _clientCount = 0;

        public ServerListener(MSLogger logger)
        {
            _logger = logger;
            _thread = new Thread(new ThreadStart(Listen));
            _event = new ManualResetEvent(false);
            _synch = new object();
            _sessions = new ArrayList();
        }

        public void Start()
        {            
            try
            {
                _logger.Debug("Starting SSH Server Listener");
                _socketListener = new TcpListener(IPAddress.Parse("127.0.0.1"), 22);
                _socketListener.Start();
                _thread.Start();
                _event.Set();
                _logger.Debug("SSH Server Listener Started");
            }
            catch (Exception ex)
            {
                _logger.Debug(String.Format("ERROR - Starting SSH Server Listener.\r\n\r\n{0}", ex.GetFullException()));
            }
        }

        public void Stop()
        {
            try
            {
                if (_socketListener != null)
                {
                    _logger.Debug("Stopping SSH Server Listener");

                    _socketListener.Stop();

                    _logger.Debug("SSH Server Listener stopped");
                }
            }
            catch (Exception ex)
            {
                _logger.Debug(String.Format("ERROR - Stopping SSH Server Listener.\r\n\r\n{0}", ex.GetFullException()));
            }
        }

        public void Dispose()
        {
            if (_thread != null && _thread.ThreadState != ThreadState.Unstarted)
            {
                _event.Reset();
                Stop();

                // kill thread
                _thread.Abort();
                _thread.Join(1000);
                _event.Close();

                // kill sockets (sessions)
                lock (_synch)
                {
                    for (var i = 0; i < _sessions.Count; i++)
                    {
                        var s = (SSHSession)_sessions[i];
                        s.SessionClosed -= new SSHSession.SessionClosedHandler(onSessionClosed);
                        s.CloseSession();
                        s.Dispose();
                    }

                    _sessions.Clear();
                }

                _thread = null;
            }
        }

        private void Listen()
        {
            while(_event.WaitOne())
            {
                var socket = acceptSocket();

                if (socket != null)
                {
                    _logger.Debug("Socket is available for connection");

                    lock (_synch)
                    {
                        if (_sessions.Count < _maxSocketSessions)
                        {
                            var session = new SSHSession(socket, ++_clientCount, _logger);
                            session.SessionClosed += new SSHSession.SessionClosedHandler(onSessionClosed);
                            _sessions.Add(session);

                            _logger.Debug("Socket connection accepted");

                            session.Start();
                        }
                        else
                        {
                            _logger.Debug("Connection rejected. Reason: Maximum Socket sessions exceeded");
                            try
                            {
                                socket.Shutdown(SocketShutdown.Both);
                                socket.Close();
                            }
                            catch (Exception ex)
                            {
                                _logger.Debug(String.Format("ERROR - Failed to close connection. {0}", ex.GetFullException()));
                            }
                        }
                    }
                }
                else 
                {
                    Thread.Sleep(500);
                }
            }
        }

        private Socket acceptSocket()
        {
            Socket socket = null;
            try
            {
                if (_socketListener.Pending())
                {
                    socket = _socketListener.AcceptSocket();
                }
            }
            catch (Exception ex)
            {
                _logger.Debug(String.Format("ERROR - Accepting socket connection request.\r\n\r\n{0}", ex.GetFullException()));
            }
            return socket;
        }

        private void onSessionClosed(SSHSession sender)
        {
            lock (_synch)
            {
                _sessions.Remove(sender);
            }
        }
    }
}
